package work.ib.apt.perf;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import utils.MyUtils;

public abstract class TestMain {

	protected int threadCount = 1000;
	protected int threadsPerSecond = 3;
	protected int parallelThreads = 50;

	public abstract Map<String,Object> getParameters();
	public abstract String getURL();
	protected final Random rnd = new Random();


	public void runTest() {
		System.out.println(new Date());
		try {
			ThreadPoolExecutor executor = new ThreadPoolExecutor(getParallelThreads(),getParallelThreads(),300L,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());
			int totalThreads = 0;
			while (totalThreads < getThreadCount()) {
				for (int i = 0; i < getThreadsPerSecond(); i++) {
					totalThreads++;
					executor.execute(new MyRunnable(totalThreads,getParameters(),executor,getURL()));
				}
				Thread.sleep(1000);
			}

			while (!executor.awaitTermination(300L, TimeUnit.SECONDS)) {}

		} catch (Exception e) {
			e.printStackTrace(System.out);
		}
		System.out.println(new Date());
	}

	public int getThreadCount() {
		return threadCount;
	}

	public int getThreadsPerSecond() {
		return threadsPerSecond;
	}

	public int getParallelThreads() {
		return parallelThreads;
	}

	static class MyRunnable implements Runnable {

		private Map<String,Object> params;
		private final ThreadPoolExecutor executor;
		private final String url;
		private final int threadNumber;

		public MyRunnable(int threadNumber, Map<String,Object> params, ThreadPoolExecutor executor, String url) {
			this.params = params;
			this.executor = executor;
			this.url = url;
			this.threadNumber = threadNumber;
		}

		@Override
		public void run() {
			try {
				Thread.sleep(new Random().nextInt(1000)+1);
				String queryString = createQueryString();
				String uri = url + "?" + queryString;
				System.out.println(uri);
				String response = getResponse(uri);
				System.out.println(threadNumber );
				if (threadNumber % 100 == 0) {
					//System.out.println(response);
				}
			} catch (Exception e) {
				e.printStackTrace(System.out);
			} finally {
				System.out.println("Active threads " + executor.getActiveCount());
				// This should probably be synchronized
				if (executor.getActiveCount() == 1 && executor.getQueue().isEmpty()) {
					executor.shutdown();
				}
			}
		}

		// Dirty, I should use try/catch/finally and close connections
		private String getResponse(String uri) throws IOException {
			URL url = new URL(uri);
			URLConnection con = url.openConnection();
			InputStream input = con.getInputStream();
			StringWriter writer = new StringWriter();
			MyUtils.copy(input, writer);
			String body = writer.toString();
			return body;
		}

		private String createQueryString() {
			StringBuilder sb = new StringBuilder();

			for (Entry<String, Object> entry : params.entrySet()) {
				if (sb.length() != 0) {
					sb.append("&");
				}
				try {
					sb.append(entry.getKey()).append("=").append(URLEncoder.encode(String.valueOf(entry.getValue()),"UTF-8"));
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace(System.out);
				}
			}

			return sb.toString();
		}

	}
}
